package cn.rokhdelar.umbrsapi.core.service.impl;


import cn.rokhdelar.umbrsapi.core.base.BaseRepo;
import cn.rokhdelar.umbrsapi.core.dto.PageResult;
import cn.rokhdelar.umbrsapi.core.entity.AbstractEntity;
import cn.rokhdelar.umbrsapi.core.jpa.FilterMap;
import cn.rokhdelar.umbrsapi.core.jpa.ParameterParser;
import cn.rokhdelar.umbrsapi.core.jpa.nativesql.QueryHelper;
import cn.rokhdelar.umbrsapi.core.jpa.nativesql.UpdateHelper;
import cn.rokhdelar.umbrsapi.core.service.BaseService;
import cn.rokhdelar.umbrsapi.core.utils.BeanUtils;
import cn.rokhdelar.umbrsapi.core.utils.FieldUtil;
import cn.rokhdelar.umbrsapi.core.utils.NullUtil;
import cn.rokhdelar.umbrsapi.core.utils.SpringUtil;
import com.alibaba.fastjson2.JSONArray;
import com.google.common.collect.Streams;
import jakarta.persistence.EntityManager;
import jakarta.persistence.TypedQuery;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Root;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.Session;
import org.hibernate.query.Query;
import org.hibernate.query.sql.internal.NativeQueryImpl;
import org.hibernate.transform.Transformers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.data.domain.*;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;


import java.lang.reflect.Field;
import java.math.BigInteger;
import java.util.*;
import java.util.Map.Entry;
import java.util.stream.Collectors;

public abstract class BaseServiceImpl<T extends AbstractEntity> implements BaseService<T>
{

	public Logger log = LoggerFactory.getLogger(BaseServiceImpl.class);

	/**
	 * 实现此方法,返回对应的实体对象
	 * 
	 * @return
	 */
	protected abstract Class<T> getEntityClass();
	
	
	private BaseRepo<T,Long> baseRepo;
	
	private BaseService<T> baseService;

	@Autowired
	private  EntityManager entityManager;
	
	private boolean cacheable = false;
	
	public Session getSession() {
		return entityManager.unwrap(Session.class);
	}
	
	
	
	@Override
	public T doDisabledById(Long id,Integer status) {
		T entity = this.findByPrimaryKey(id);
		return this.doUpdateSelective(entity);
	}
	
	/**
	 * 对MAP参数解析
	 */
	private ParameterParser parameterParser=new ParameterParser();
	
	private BaseRepo<T,Long> getBaseRepo() {
		String repoName = StringUtils.uncapitalize(this.getEntityClass().getSimpleName()) + "Repo";
		if (repoName != null) {
			log.debug("find baseRepo=" + repoName);
			baseRepo = (BaseRepo<T,Long>) SpringUtil.getBean(repoName);
		}
		if (baseRepo == null) {
			log.error("Not found bean defined for '" + repoName + "'");
		}
		return baseRepo;
	}

	public BaseService<T> getBaseSerice() {
		String repoName = StringUtils.uncapitalize(this.getEntityClass().getSimpleName()) + "Service";
		if (repoName != null) {
			log.debug("find baseService=" + repoName);
			baseService = (BaseService<T>) SpringUtil.getBean(repoName);
		}
		if (baseService == null) {
			log.error("Not found bean defined for '" + repoName + "'");
		}
		return baseService;
	}

	public void evict(T entity) {
		entityManager.detach(entity);
	}

	/**
	 * 插入
	 */
	@Override
	public T doInsert(T entity) {
		Assert.notNull(entity, "Entities must not be null!");
		getBaseRepo().save(entity);
		return entity;
	}


	/*
	 * (non-Javadoc)
	 * @see org.springframework.data.jpa.repository.JpaRepository#save(java.lang.Iterable)
	 */
	@Transactional
	@Override
	public List<T> doInsertAll(Iterable<T> entities){
		Assert.notNull(entities, "Entities must not be null!");

		List<T> result = new ArrayList<T>();

		for (T entity : entities) {
			result.add(entity);
		}
		getBaseRepo().saveAll(entities);
		
		for (T entity : entities) {
			this.afterDoInsert(entity);
			result.add(entity);
		}
		return result;
	}
	/**
	 * after DoUpdatel trigger
	 * 
	 * @param entity
	 */
	protected void afterDoInsert(T entity) {

	}
	
	


	/**
	 * after DoUpdatel trigger
	 * 
	 * @param entity
	 */
	protected void afterDoUpdate(T entity) {

	}

	/**
	 * 提取的公共方法
	 * 
	 * @param entity
	 * @return
	 */
	@Override
	public T doSave(T entity) {
		Assert.notNull(entity, "Entities must not be null!");
		if(Objects.isNull(entity.getId())){
			this.doInsert(entity);
		}else{
			this.doUpdateSelective(entity);
		}
		return entity;
	}

	/**
	 * 提取的公共更新方法
	 * 
	 * @param entity
	 */
	@Override
	public T doUpdate(T entity) {
		Assert.notNull(entity, "Entities must not be null!");
		getBaseRepo().save(entity);
		this.afterDoUpdate(entity);
		return entity;
	}

	@Override
	public List<T> doUpdateAll(Collection<T> entities) {
		Assert.notNull(entities, "Entities must not be null!");

		List<T> result = new ArrayList<T>();
		getBaseRepo().saveAll(entities);
		for (T entity : entities) {
			this.afterDoUpdate(entity);	
			result.add(entity);
		}
		return result;

	}

	/**
	 * 更新实体 ,为空的属性不更新
	 */
	@Override
	public T doUpdateSelective(T entity) {
		Long id = entity.getId();
	    if (id != null) {
	        Optional<T> op = getBaseRepo().findById(id);
	        if (op.isPresent()) {
	            T t = op.get();
	            BeanUtils.copyPropertiesWithoutNull(entity, t);
	            entity = (T) t;
	        }
	    }
	    return doUpdate(entity);
	}

	/**
	 * 更新集合,为空的属性不更新
	 */
	@Override
	public List<T> doUpdateAllSelective(final Collection<T> entities) {
		Assert.notNull(entities, "Entities must not be null!");

		List<T> result = new ArrayList<T>();

		for (T entity : entities) {
			result.add(doUpdateSelective(entity));
		}
		
		return result;
	}

	/**
	 * 查找所有记录,返回LIST
	 */
	@Override
	public List<T> findAll() {
		return (List<T>)getBaseRepo().findAll();
	}

	@Override
	public void doDelete(T entity){
		getBaseRepo().delete(entity);
	}

    @Override
	public void doDeleteByParams(FilterMap filterMap) {
		List<T> list=findByParams(filterMap, null);
		for (T t : list) {
			doDelete(t);
		}
	}

	@Override
	public void doDeleteAllByPrimaryKey(final Collection<Long> ids){
		for (Long id : ids) {
			getBaseRepo().deleteById(id);
		}
	}
	@Override
	public void doDeleteByPrimaryKey(Long id){
		getBaseRepo().deleteById(id);
	}
	
	@Override
	public T doLogicDelete(T entity) {
		return this.doUpdateSelective(entity);
	}

	@Override
	public T doLogicDeleteByPrimaryKey(Long id) {
		if(NullUtil.isNull(id)){
			return null;
		}
		T entity = this.findByPrimaryKey(id);
		return this.doUpdateSelective(entity);
	}
	
	@Override
	public void doLogicDeleteByEntitys(final Collection<T> entities) {
		Assert.notNull(entities, "Entities must not be null!");
		for (T entity : entities) {
			doLogicDeleteByPrimaryKey(entity.getId());
		}
	}
	
    @Override
	public void doLogicDeleteAllByPrimaryKey(List<Long> ids) {
		for (Long id : ids) {
			doLogicDeleteByPrimaryKey(id);
		}
	}

	/**
	 * 根据条件逻辑删除
	 * 
	 * @return
	 */
    @Override
	public void doLogicDeleteByParams(FilterMap filterMap) {
		List<T> list=findByParams(filterMap,null);
		for (T t : list) {
			doLogicDelete(t);
		}
	}
	
    
	@Override
	public T findByPrimaryKey(Long id){
		Optional<T> result=getBaseRepo().findById(id);
		if(result.isPresent()){
			return result.get();
		}
		return null;
	}

	@Override
	public List<T> findByPrimaryKeys(List ids){
		Iterable elements=getBaseRepo().findAllById(ids);
		return elements == null ? null : (ArrayList) Streams.stream(elements).collect(Collectors.toCollection(ArrayList::new));
	}
	
	@Override
    public int findCountByParams(FilterMap filterMap){
		// 获取session
		Session session = getSession();
		CriteriaBuilder builder = session.getCriteriaBuilder();
		CriteriaQuery<Long> countQuery = builder.createQuery(Long.class);
		Root<?> root = countQuery.from(getEntityClass());
		// 添加条件
		parameterParser.addCriterion(builder, countQuery, root, filterMap,null);

		countQuery.select(builder.count(root));

		Query<Long> query = session.createQuery(countQuery);
		return query.getSingleResult().intValue();

    }

	@Override
	public T findUniqueByParams(FilterMap filterMap){
		
		List<T> list = findByParams(filterMap, null);
		if (list.size() == 1) {
			return list.get(0);
		} else if (list.size() == 0) {
			return null;
		} else {
			throw new IncorrectResultSizeDataAccessException(1, list.size());
		}
	}

	
	@Override
	public List<T> findByParams(FilterMap filterMap,Sort sort){
		Session session = getSession();
		CriteriaBuilder builder = session.getCriteriaBuilder();
		CriteriaQuery query = builder.createQuery(getEntityClass());
		Root<?> root = query.from(getEntityClass());
		// 添加条件
		parameterParser.addCriterion(builder, query, root, filterMap,sort);
		query.select(root);
		List<T> resultList = session.createQuery(query).getResultList();

		return resultList;
	}
	
	@Override
	public List<T> findByParams(FilterMap filterMap){
		Session session = getSession();
		CriteriaBuilder builder = session.getCriteriaBuilder();
		CriteriaQuery query = builder.createQuery(getEntityClass());
		Root<?> root = query.from(getEntityClass());
		// 添加条件
		parameterParser.addCriterion(builder, query, root, filterMap,null);
		query.select(root);
		List<T> resultList = session.createQuery(query).getResultList();

		return resultList;
	}
	
	@Override
	public List<T> findByParams(FilterMap filterMap, Sort sort,int limit){
		Session session = getSession();
		CriteriaBuilder builder = session.getCriteriaBuilder();
		CriteriaQuery query = builder.createQuery(getEntityClass());
		Root<?> root = query.from(getEntityClass());
		// 添加条件
		parameterParser.addCriterion(builder, query, root, filterMap,null);
		query.select(root);
		List<T> resultList = session.createQuery(query).setMaxResults(limit).getResultList();
		return resultList;
	}
	
	@Override
	public List<Map<String,Object>> findMapByParams(FilterMap filterMap,Sort sort){
		Session session = getSession();
		CriteriaBuilder builder = session.getCriteriaBuilder();
		CriteriaQuery query = builder.createQuery(getEntityClass());
		Root<?> root = query.from(getEntityClass());
		// 添加条件
		parameterParser.addCriterion(builder, query, root, filterMap,null);
		query.select(root);
		List<Map<String, Object>> resultList = session.createQuery(query).getResultList();
		return resultList;
	}
	
	 /**
     * 根据条件更新
     * @param params
	 * @throws IllegalAccessException 
	 * @throws IllegalArgumentException 
     * @throws Exception
     */
    @Override
	public void updateByParams(Map<String, Object> params,FilterMap filterMap) throws IllegalArgumentException, IllegalAccessException{
		List<T> list=findByParams(filterMap,null);
		for (T t : list){
			for (Entry<String, Object> entry : params.entrySet()) {
				Field field = FieldUtil.getFields(getEntityClass(), entry.getKey());
				if (field.getType() == String.class) {
					field.setAccessible(true);
					field.set(t, entry.getValue());
				} 
			}
		}
		doUpdateAll(list);
	}

    @Deprecated
	@Override
	public PageResult findByPage(FilterMap filterMap, int pageNo, int pageSize){
		Session session = getSession();
		CriteriaBuilder builder = session.getCriteriaBuilder();
		CriteriaQuery query = builder.createQuery(getEntityClass());
		Root<?> root = query.from(getEntityClass());
		// 添加条件
		parameterParser.addCriterion(builder, query, root, filterMap, null);
		query.select(root);

		// Create a pageable object for pagination
		Pageable pageable = PageRequest.of(pageNo, pageSize); // Page numbers usually start from 1

		// Execute the paginated query
		TypedQuery<T> typedQuery = session.createQuery(query);
		Page<T> page = new PageImpl<>(typedQuery.setFirstResult(pageable.getPageNumber() * pageable.getPageSize())
				.setMaxResults(pageable.getPageSize())
				.getResultList(), pageable, typedQuery.getResultList().size());

		return new PageResult(page);
	
	}
	@Deprecated
	@Override
	public PageResult findByPage(FilterMap filterMap, Sort sort, int pageNo, int pageSize){
		Session session = getSession();
		CriteriaBuilder builder = session.getCriteriaBuilder();
		CriteriaQuery query = builder.createQuery(getEntityClass());
		Root<?> root = query.from(getEntityClass());
		// 添加条件
		parameterParser.addCriterion(builder, query, root, filterMap, null);
		query.select(root);
		Pageable pageable = PageRequest.of(pageNo, pageSize,sort);
		TypedQuery<T> typedQuery = session.createQuery(query);
		Page<T> page = new PageImpl<>(typedQuery.setFirstResult(pageable.getPageNumber() * pageable.getPageSize())
				.setMaxResults(pageable.getPageSize())
				.getResultList(), pageable, typedQuery.getResultList().size());
		return new PageResult(page);


	}
	
	
	@Override
	public PageResult findByPage(FilterMap filterMap,Class<?> clazz, Sort sort,int pageNo, int pageSize){
		Session session = getSession();
		CriteriaBuilder builder = session.getCriteriaBuilder();
		CriteriaQuery query = builder.createQuery(getEntityClass());
		Root<?> root = query.from(getEntityClass());
		// 添加条件
		parameterParser.addCriterion(builder, query, root, filterMap, sort);
		query.select(root);
		Pageable pageable = PageRequest.of(pageNo, pageSize,sort);
		TypedQuery<T> typedQuery = session.createQuery(query);
		Page<T> page = new PageImpl<>(typedQuery.setFirstResult(pageable.getPageNumber() * pageable.getPageSize())
				.setMaxResults(pageable.getPageSize())
				.getResultList(), pageable, typedQuery.getResultList().size());
		return new PageResult(page);
	
	}

	@Override
	public Object findObjectBySql(QueryHelper helper) {
        Query query = (Query) entityManager.createNativeQuery(helper.getSql());
        query.unwrap(NativeQueryImpl.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
        Object o = query.getSingleResult();
        return o;
    }
	
	@Override
	public List<Map<String, Object>> findListBySql(QueryHelper helper){
		 Query query = (Query) entityManager.createNativeQuery(helper.getSql());
	     query.unwrap(NativeQueryImpl.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
	     List<Map<String, Object>> list = (List<Map<String, Object>>) query.getResultList();
	     return list;
	}
	
	@Override
	public List<T> findListBySql(QueryHelper helper,Class<?> clazz){
		 Query query = (Query)entityManager.createNativeQuery(helper.getSql());
	     query.unwrap(NativeQueryImpl.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
	     List<Map<String, Object>> list = (List<Map<String, Object>>) query.getResultList();
	     List<T>  dataList = (List<T>) JSONArray.parseArray(JSONArray.toJSONString(list),clazz);
	     return dataList;
	}
	
	@Override
	public int updateBySqlId(UpdateHelper helper){
		Query query = (Query) entityManager.createNativeQuery(helper.getSql());
	    query.unwrap(NativeQueryImpl.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
	    return query.executeUpdate();
	}
	
	@Override
	@Deprecated
	public PageResult selectPageSql(QueryHelper helper, int pageNo, int limit){
		helper.addLimit(pageNo, limit);
		PageRequest pageable = PageRequest.of(pageNo, limit);
		//查询总数
        Query countQuery = (Query) entityManager.createNativeQuery(helper.getCountQuerySql());
		Long count = (Long)countQuery.getSingleResult();
		//查询sql
        Query query = (Query) entityManager.createNativeQuery(helper.getSql());
        query.unwrap(NativeQueryImpl.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
        List<Map> list = (List<Map>) query.getResultList();
        // 返回
        Page<Map> page=new PageImpl<Map>(list, pageable, count);
		//Page<Map> page=getBaseRepo().selectPageSql(helper,pageNo,limit);
		return new PageResult(page);
	}

	@Override
	public PageResult selectPageSql(QueryHelper helper, int pageNo, int limit,Class<?> clazz){
		helper.addLimit(pageNo, limit);
		PageRequest pageable = PageRequest.of(pageNo, limit);
		//查询总数
		Query countQuery = (Query) entityManager.createNativeQuery(helper.getCountQuerySql());
		Long count = (Long)countQuery.getSingleResult();
		//查询sql
		Query query =  (Query)entityManager.createNativeQuery(helper.getSql());
		query.unwrap(NativeQueryImpl.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
		List<Map> list = (List<Map>) query.getResultList();
		// 返回
		Page<Map> page = new PageImpl<Map>(list, pageable, count);
		return new PageResult(page,clazz);
	}
	
	
	@Override
	public void addExtraPageParamters(FilterMap filterMap,Map<String, String> params){
		
	}
	
	@Override
	public void translate(List<T> entitys){
		
	}
}
